package cn.cutie.clotconfig.client.value;

import cn.cutie.clotconfig.client.util.PlaceholderHelper;
import cn.kimmking.utils.FieldUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;

/**
 * @Description: 处理springvalue
 * 1.扫描所有的 spring value，保存
 * 2.配置变更的时候，更新所有的 spring value
 *
 * @Author: Cutie
 * @CreateDate: 2024/5/11 14:22
 * @Version: 0.0.1
 */
@Slf4j
public class SpringValueProcessor implements BeanPostProcessor, BeanFactoryAware, ApplicationListener<EnvironmentChangeEvent> {

    static final PlaceholderHelper placeholderHelper = PlaceholderHelper.getInstance();
    static final MultiValueMap<String, SpringValue> VALUE_HOLDER = new LinkedMultiValueMap<>();

    @Setter
    private BeanFactory beanFactory;

    /**
     * 1.扫描所有的 spring value，保存
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        FieldUtils.findAnnotatedField(bean.getClass(), Value.class).forEach(
                field -> {
                    log.info("[CLOTCONFIG] find spring field:{}", field);
                    Value value = field.getAnnotation(Value.class);
                    Set<String> placeholders = placeholderHelper.extractPlaceholderKeys(value.value());// placeholder的key， ${clot.a}.${clot.b} or ${clot.a:111}
                    placeholders.forEach(key ->{
                        log.info("[CLOTCONFIG] find spring key:{}", key);
                        SpringValue springValue = new SpringValue(bean, beanName, key, value.value(), field);
                        VALUE_HOLDER.add(key, springValue);
                    });
                }
        );

        return bean;
    }

//    @EventListener
//    public void onChange(EnvironmentChangeEvent event){
//        event.getKeys().forEach(key -> {
//            log.info("[CLOTCONFIG] on change key:{}", key);
//            List<SpringValue> springValues = VALUE_HOLDER.get(key);
//            if (springValues == null || springValues.isEmpty()){
//                return;
//            }
//            springValues.forEach(springValue -> {
//                log.info("[CLOTCONFIG] on change key:{}, spring value:{}", key, springValue);
//                try{
//                    Object value = placeholderHelper.resolvePropertyValue((ConfigurableBeanFactory) beanFactory,
//                            springValue.getBeanName(), springValue.getPlaceholder());
//                    log.info("[CLOTCONFIG] on change key:{}, spring value:{}, value:{}, holder:{}",
//                            key, springValue, value, springValue.getPlaceholder());
//                    springValue.getField().setAccessible(true);
//                    springValue.getField().set(springValue.getBean(), value);
//                } catch (Exception e){
//                    log.error("[CLOTCONFIG]  set spring value error", e);
//                }
//            });
//        });
//    }

    /**
     * 2.配置变更的时候，更新所有的 spring value
     * 等价于 @EventListener
     * ApplicationListener<EnvironmentChangeEvent>
     * @param event
     */
    @Override
    public void onApplicationEvent(EnvironmentChangeEvent event) {
        log.info("[CLOTCONFIG] udpate spring value for keys:{}", event.getKeys());
        event.getKeys().forEach(key -> {
            log.info("[CLOTCONFIG] on change key:{}", key);
            List<SpringValue> springValues = VALUE_HOLDER.get(key);
            if (springValues == null || springValues.isEmpty()){
                return;
            }
            springValues.forEach(springValue -> {
                log.info("[CLOTCONFIG] on change key:{}, spring value:{}", key, springValue);
                try{
                    Object value = placeholderHelper.resolvePropertyValue((ConfigurableBeanFactory) beanFactory,
                            springValue.getBeanName(), springValue.getPlaceholder());
                    log.info("[CLOTCONFIG] on change key:{}, spring value:{}, value:{}, holder:{}",
                            key, springValue, value, springValue.getPlaceholder());
                    springValue.getField().setAccessible(true);
                    springValue.getField().set(springValue.getBean(), value);
                } catch (Exception e){
                    log.error("[CLOTCONFIG]  set spring value error", e);
                }
            });
        });
    }

}
